using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class TestPCIO : RapidHardware
    {

        PipelineSerialAssembler oAssembler;
        public override bool ConfigureSettings()
        {
            Settings.UseStart = false;
            Settings.BuildHardware = true;
            Settings.ForceBuildHardware = false;
            Settings.RunFromXilinx = true;
            Settings.ClockFrequency = 10;
            Settings.UseSDRAM = true;
            Settings.BuildRemote = false;
            return true;
        }

        public override bool GenerateStructure(TopLevelComponent TopLevelComponent)
        {
            // why required?
            TopLevelComponent.FIFOInterface.Enabled = true;
            TopLevelComponent.TestBusWrite.Join(0);

            //Utility.SinkExtraBits(TopLevelComponent, TopLevelComponent.FIFOInterface.InputFIFOData);
            //Utility.SinkExtraBits(TopLevelComponent, TopLevelComponent.FIFOInterface.InputStatusReg);
            //Utility.SinkExtraBits(TopLevelComponent, TopLevelComponent.TestBusRead);

            //SystemInterface.AddSignalSource("fifo_out", TopLevelComponent.FIFOInterface.OutputFIFOData, 0);
            //SystemInterface.AddSignalSource("fifo_write", TopLevelComponent.FIFOInterface.OutputFIFOWrite, 0);
            //SystemInterface.AddSignalSource("fifo_read", TopLevelComponent.FIFOInterface.OutputFIFORead, 0);

            //SystemInterface.AddSignalSource("pipe_data_out", TopLevelComponent.FIFOInterface.BusFromInternal.Data, 0);
            //SystemInterface.AddSignalSource("pipe_data_out_ready", TopLevelComponent.FIFOInterface.BusFromInternal.DataReady, 0);
            //SystemInterface.AddSignalSource("pipe_data_in_stall", TopLevelComponent.FIFOInterface.BusFromExternal.Stall, 0);

            PipelineBuffer oInBuffer = new PipelineBuffer(TopLevelComponent, "in_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 18, 4);
            PipelineBuffer oOutBuffer = new PipelineBuffer(TopLevelComponent, "out_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 16, 4);
            PipelineBuffer oDataBuffer = new PipelineBuffer(TopLevelComponent, "data_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 64, 4);
            PipelineBuffer oShrinkBuffer = new PipelineBuffer(TopLevelComponent, "shrink_buffer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 48, 4);

            PipelineStage oPipeStage = new PipelineStage(TopLevelComponent, "pipe_sync", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 16);

            //oPipeSync.OutputBus.Join(TopLevelComponent.FIFOInterface.BusFromInternal);

            oInBuffer.InputBus.Join(TopLevelComponent.FIFOInterface.BusFromExternal);
            oAssembler = new PipelineSerialAssembler(TopLevelComponent, "assembler", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 18, 16, 2, 4);
            oAssembler.InputBus.Join(oInBuffer.OutputBus);
            oAssembler.OutputBus.Join(oDataBuffer.InputBus);


            PipelineReMap oPipeMap = new PipelineReMap(oDataBuffer.OutputBus, oShrinkBuffer.InputBus);
            oPipeMap.DefineMap(32, 23, 0, true);
            oPipeMap.DefineMap(0, 5, 32, true);
            oPipeMap.CompleteMap();
            oShrinkBuffer.InputBus.Data[38, 47].Join(Conversion.StringBinaryToInt("1111111111"));

            //oShrinkBuffer.OutputBus.Data.ConnectSubset(23, 9, Operators.CONST(this, 0, 9), 0, 9);

            Utility.SinkExtraBits(TopLevelComponent, oDataBuffer.OutputBus.Data);

            PipelineSerializer oSerializer = new PipelineSerializer(TopLevelComponent, "serializer", TopLevelComponent.MainClock, TopLevelComponent.MainReset, 48, 14);
            oSerializer.InputBus.Join(oShrinkBuffer.OutputBus);
            oSerializer.OutputBus.Join(TopLevelComponent.FIFOInterface.BusFromInternal);
            oSerializer.OffsetAddress = true;

            return true;
        }

        public override bool ConfigureSignalViews(TopLevelComponent TopLevelComponent)
        {
            //SystemInterface.AddSignalView("fifo_status", "fifo_ifc.i_status_reg");
            //SystemInterface.AddSignalView("fifo_in", "fifo_ifc.i_fifo_data");

            //SystemInterface.AddSignalView("fifo_in", poTopLevelComponent.FIFOInterface.InputFIFOData,0,14,true);
            //SystemInterface.AddSignalView("fifo_seq", poTopLevelComponent.FIFOInterface.InputFIFOData, 16, 17,true);
            //SystemInterface.AddSignalView("fifo_status", poTopLevelComponent.FIFOInterface.InputStatusReg);
            //SystemInterface.AddSignalView("test", poTopLevelComponent.FIFOInterface.Test);

            SystemInterface.AddSignalView("pipe_data_in", TopLevelComponent.FIFOInterface.BusFromExternal.Data);
            SystemInterface.AddSignalView("pipe_data_in_ready", TopLevelComponent.FIFOInterface.BusFromExternal.DataReady);
            SystemInterface.AddSignalView("pipe_data_out_stall", TopLevelComponent.FIFOInterface.BusFromInternal.Stall);

            SystemInterface.AddSignalView("pipe_data_out", TopLevelComponent.FIFOInterface.BusFromInternal.Data);
            SystemInterface.AddSignalView("pipe_data_out_ready", TopLevelComponent.FIFOInterface.BusFromInternal.DataReady);
            SystemInterface.AddSignalView("pipe_data_in_stall", TopLevelComponent.FIFOInterface.BusFromExternal.Stall);

            SystemInterface.AddSignalView("state_reset", oAssembler.oStateMachine.StateCurrent("state_reset"));
            SystemInterface.AddSignalView("state_0", oAssembler.oStateMachine.StateCurrent("state_0"));
            SystemInterface.AddSignalView("state_1", oAssembler.oStateMachine.StateCurrent("state_1"));
            SystemInterface.AddSignalView("state_2", oAssembler.oStateMachine.StateCurrent("state_2"));
            SystemInterface.AddSignalView("state_final", oAssembler.oStateMachine.StateCurrent("state_final"));
            SystemInterface.AddSignalView("address", oAssembler.nvAddress);

            return true;
        }

        public void ShowFIFO(HardwareScript poScript, int piTime)
        {
            poScript.Echo(piTime,"======================================= Time" + piTime.ToString());
            poScript.SignalView(piTime, "pipe_data_in");
            poScript.SignalView(piTime, "pipe_data_in_ready");
            poScript.Echo(piTime, "--");
            poScript.SignalView(piTime, "pipe_data_out");
            poScript.SignalView(piTime, "pipe_data_out_ready");
            poScript.Echo(piTime, "...");
            poScript.SignalView(piTime, "address");            
            poScript.SignalView(piTime, "state_reset");
            poScript.SignalView(piTime, "state_0");
            poScript.SignalView(piTime, "state_1");
            poScript.SignalView(piTime, "state_2");
            poScript.SignalView(piTime, "state_final");
        }

        public override bool GenerateHardwareScripts()
        {
            HardwareScript oScript = HardwareScripts.NewScript("test");
            oScript.NoReset = true;

            for (int iTime = 0; iTime < 10; iTime++)
                ShowFIFO(oScript, iTime);
            return true;
        }

        public static void PCTest()
        {
            const int BLOCKS = 2;

            MemoryBlock oMemoryBlock = new MemoryBlock();

            for (int iX = 0; iX < BLOCKS; iX++)
                oMemoryBlock.RequestMemory(iX + 16);

            IOInterface.PostMemory(ref oMemoryBlock);

            string sResult = "";
            for (int iX = 0; iX < BLOCKS; iX++)
            {
                int iAddress = iX + 16;
                sResult += iAddress.ToString() + " = " + oMemoryBlock.ReturnMemory(iX + 16).ToString() + "\n";
            }

            System.Windows.Forms.MessageBox.Show(sResult);
        }

        public static void PCIOPerSec()
        {
            const bool MEM_CHECK = false;
            const int LOOPS = 1000000 / IOInterface.OUTPUT_BLOCK_WORDS_MAX / 32 * 5;
            //const int LOOPS = 100;

            string sResult = "";

            int iErrorCount = 0;
            int iMemoryError = 0;

            Timer oTimer = new Timer();
            oTimer.Start();


            int iBlockSize = IOInterface.OUTPUT_BLOCK_WORDS_MAX;
            MemoryBlock oMemoryBlock = new MemoryBlock();

            for (int iLoop = 0; iLoop < LOOPS; iLoop++)
            {
                oMemoryBlock.Reset();
//                if (iLoop == 7)
//                    Console.WriteLine("7");

                for (int iMemIdx = 0; iMemIdx < iBlockSize; iMemIdx++)
                {
                    if (!MEM_CHECK)
                        oMemoryBlock.RequestMemory(iMemIdx);
                    else
                        oMemoryBlock.RequestMemory(iMemIdx + 3497301);
                }
                IOInterface.PostMemory(ref oMemoryBlock);
                if (MEM_CHECK)
                {
                    for (int iMemIdx = 0; iMemIdx < iBlockSize; iMemIdx++)
                    {
                        if (oMemoryBlock.ReturnMemory(3497301 + iMemIdx) != iMemIdx + 3497301)
                        {
                            iMemoryError++;
                            int iMem = 3497301 + iMemIdx;
                            Console.WriteLine(iLoop.ToString() + ": MEM ERROR " + iMem.ToString() + " != " + oMemoryBlock.ReturnMemory(3497301 + iMemIdx).ToString());
                        }
                    }
                }
                if (!oMemoryBlock.IsReady)
                {
                   //System.Windows.Forms.MessageBox.Show(oMemoryBlock.DebugDisplay());
                    Console.WriteLine(iLoop.ToString() + " ERROR " + oMemoryBlock.OpenRequests.ToString());
                   iErrorCount++;
                }
            }
            oTimer.Stop();

            int iIOCount = LOOPS * iBlockSize;
            int iIOBits = 32 * iIOCount;
            double dIOPerSec = (double)iIOCount / (double)oTimer.Duration;
            double dIOBitsPerSec = (double)iIOBits / (double)oTimer.Duration;
            double dSecPerMillion = 1 / dIOBitsPerSec * 1000000;
            double dErrorRate = (double)iErrorCount / (double)LOOPS;
            double dMemoryErrorRate = (double)iMemoryError / (double)iIOCount;

            sResult = "IO Per Sec = " + dIOPerSec.ToString() + "\n";
            sResult += "IO Bits Per Sec = " + dIOBitsPerSec.ToString() + "\n";
            sResult += "Sec Per IO Million = " + dSecPerMillion.ToString() + "\n";
            sResult += "Error Rate = " + dErrorRate.ToString() + "\n";
            sResult += "Errors = " + iErrorCount.ToString();
            if (MEM_CHECK)
            {
                sResult += "\nMem Error Rate = " + dMemoryErrorRate.ToString() + "\n";
                sResult += "Mem Errors = " + iMemoryError.ToString();
            }


            System.Windows.Forms.MessageBox.Show(sResult);
        }

    }
}


